import { User, $User, UserId } from './model';
import { mainDB } from '../../infra/mysql';
import { RowDataPacket } from 'mysql2';

export interface UserRepository {
  /**
   * 根据用户 ID 查询用户
   *
   * @param userId 用户 ID
   */
  selectByUserId(userId: UserId): Promise<$User>;

  /**
   * 根据用户名查询用户
   *
   * @param username 用户名
   */
  selectByUsername(username: string): Promise<$User>;

  /**
   * 更新用户信息。
   *
   * 如果被更新的用户不存在于数据库中，那么本函数不对这种情况做任何保证
   *
   * @param user 被更新的用户
   */
  updateUser(user: User): Promise<void>;

  /**
   * 插入新的用户信息。这里“新的”是指 `userId < 1`。
   *
   * @param user 被插入的用户信息的数据库版本。
   */
  insertUser(user: User): Promise<User>;
}

class UserMySQLRepository implements UserRepository {
  async selectByUserId(userId: number): Promise<$User> {
    const sql = `
      select id, username, nick_name as nickName from user
      where id = ?
    `;
    const $user = await mainDB.queryOne(sql, [userId]);
    return !$user ? undefined : this.toUser($user);
  }

  async selectByUsername(username: string): Promise<$User> {
    const sql = `
      select id, username, nick_name as nickName from user
      where username = ?
    `;
    const $user = await mainDB.queryOne(sql, [username]);
    return !$user ? undefined : this.toUser($user);
  }

  private toUser(row: RowDataPacket): User {
    return {
      id: row['id'],
      username: row['username'],
      nickName: row['nickName'],
    };
  }

  async updateUser(user: User): Promise<void> {
    const sql = 'update user set nickname = ? where id = ?';
    await mainDB.execute(sql, [user.username, user.id]);
  }

  async insertUser(user: User): Promise<User> {
    if (user.id >= 1) {
      throw new Error('不支持插入');
    }

    const sql = 'insert into user set username = ?, nick_name = ?';
    const userId = await mainDB.insertOneWithAutoIncrement(sql, [user.username, user.nickName]);
    return (await this.selectByUserId(userId))!;
  }
}

export const createUserRepository = (): UserRepository => new UserMySQLRepository();
